﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using System.IO;

namespace Aphysoft.Vate.Graphics
{
    /// <summary>
    /// Texture class helping you with using DirectX Textures and handling
    /// possible errors that can happen while loading (stupid DirectX
    /// error messages telling you absolutly nothing, so a lot of pre-checks
    /// help you determinate the error before even calling DirectX methods).
    /// </summary>
    public class GameTexture : IDisposable
    {
        #region Properties

        /// <summary>
        /// Texture filename
        /// </summary>
        protected string filename = "";
        /// <summary>
        /// Get filename of texture.
        /// </summary>
        public string Filename
        {
            get
            {
                return filename;
            }
        }
        /// <summary>
        /// Size of texture.
        /// </summary>
        protected int width, height;
        /// <summary>
        /// Width of texture.
        /// </summary>
        public int Width
        {
            get
            {
                return width;
            }
        }
        /// <summary>
        /// Height of texture.
        /// </summary>
        public int Height
        {
            get
            {
                return height;
            }
        }
        /// <summary>
        /// Rectangle.
        /// </summary>
        /// <returns>Rectangle</returns>
        public Rectangle Rectangle
        {
            get
            {
                return new Rectangle(0, 0, width, height);
            }
        }
        /// <summary>
        /// Size of half a pixel, will be calculated when size is set.
        /// </summary>
        private Vector2 precaledHalfPixelSize = Vector2.Zero;
        /// <summary>
        /// Get the size of half a pixel, used to correct texture
        /// coordinates when rendering on screen, see Texture.RenderOnScreen.
        /// </summary>
        public Vector2 HalfPixelSize
        {
            get
            {
                return precaledHalfPixelSize;
            }
        }
        /// <summary>
        /// Calc half pixel size
        /// </summary>
        protected void CalcHalfPixelSize()
        {
            precaledHalfPixelSize = new Vector2(
                (1.0f / (float)width) / 2.0f,
                (1.0f / (float)height) / 2.0f);
        }
        /// <summary>
        /// XNA Framework Graphic Texture
        /// </summary>
        protected Texture2D internalTexture;
        /// <summary>
        /// XNA Framework texture
        /// </summary>
        public virtual Texture2D Texture
        {
            get
            {
                return internalTexture;
            }
        }

        /// <summary>
        /// Loading succeeded?
        /// </summary>
        protected bool loaded = true;

        /// <summary>
        /// Error?
        /// </summary>
        protected string error = "";

        /// <summary>
        /// Is texture valid? Will be false if loading failed.
        /// </summary>
        public virtual bool Valid
        {
            get
            {
                return loaded && internalTexture != null;
            }
        }
        /// <summary>
        /// Has alpha?
        /// </summary>
        protected bool hasAlpha = false;
        /// <summary>
        /// Has texture alpha information?
        /// </summary>
        public bool HasAlphaPixels
        {
            get
            {
                return hasAlpha;
            }
        }
        #endregion

        #region Constructor
        /// <summary>
        /// Create texture from given filename.
        /// </summary>
        /// <param name="filename">Filename, relative with Content/, use PathHelper.CreatePath or CreateBuiltInPath.</param>
        public GameTexture(string filename)
        {
            if (String.IsNullOrEmpty(filename))
                throw new ArgumentNullException("filename", "Unable to create texture without valid filename.");

            // Set content name (cut off extension!)
            this.filename = Path.GetFileNameWithoutExtension(filename);

            // Try loading as 2d texture
            internalTexture = Vate.Load<Texture2D>(filename);

            // Get info from the texture directly.
            width = internalTexture.Width;
            height = internalTexture.Height;

            // We will use alpha for Dxt3 and Dxt5 textures.
            hasAlpha = (internalTexture.Format == SurfaceFormat.Dxt5 || internalTexture.Format == SurfaceFormat.Dxt3);

            loaded = true;

            CalcHalfPixelSize();
        }

        /// <summary>
        /// Create texture, protected version for derived classes.
        /// </summary>
        protected GameTexture()
        {
        }

        /// <summary>
        /// Create texture by just assigning a Texture2D.
        /// </summary>
        /// <param name="texture">Texture</param>
        public GameTexture(Texture2D texture)
        {
            if (texture == null)
                throw new ArgumentNullException("texture");

            internalTexture = texture;

            // Get info from the texture directly.
            width = internalTexture.Width;
            height = internalTexture.Height;

            loaded = true;

            // We will use alpha for Dxt3 and Dxt5 textures
            hasAlpha = (internalTexture.Format == SurfaceFormat.Dxt5 ||  internalTexture.Format == SurfaceFormat.Dxt3);

            CalcHalfPixelSize();
        }
        #endregion

        #region Disposing
        /// <summary>
        /// Dispose
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// Dispose
        /// </summary>
        /// <param name="disposing">Disposing</param>
        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (internalTexture != null)
                    internalTexture.Dispose();
                internalTexture = null;
            }

            loaded = false;
        }
        #endregion

        #region Draw Helper
        /// <summary>
        /// Draw texture.
        /// </summary>
        /// <param name="destinationRectangle">Destination rectangle</param>
        /// <param name="sourceRectangle">Source rectangle (nullable)</param>
        public void Draw(Rectangle destinationRectangle, Rectangle? sourceRectangle)
        {
            Vate.GlobalAlphaSpriteBatch.Draw(internalTexture, destinationRectangle, sourceRectangle, Color.White);
        }
        /// <summary>
        /// Draw texture.
        /// </summary>
        /// <param name="destinationRectangle">Destination rectangle</param>
        /// <param name="sourceX">Source x</param>
        /// <param name="sourceY">Source y</param>
        /// <param name="sourceWidth">Source width</param>
        /// <param name="sourceHeight">Source height</param>
        public void Draw(Rectangle destinationRectangle, int sourceX, int sourceY, int sourceWidth, int sourceHeight)
        {
            Vate.GlobalAlphaSpriteBatch.Draw(internalTexture, destinationRectangle, new Rectangle(sourceX, sourceY, sourceWidth, sourceHeight), Color.White);
        }

        /// <summary>
        /// Draw texture.
        /// </summary>
        /// <param name="position">Position</param>
        public void Draw(Point position)
        {
            Vate.GlobalAlphaSpriteBatch.Draw(internalTexture,
                new Rectangle(position.X, position.Y, width, height),
                new Rectangle(0, 0, width, height), Color.White);
        }
        /// <summary>
        /// Draw texture.
        /// </summary>
        /// <param name="destinationRectangle">Destination rectangle</param>
        public void Draw(Rectangle destinationRectangle)
        {
            Vate.GlobalAlphaSpriteBatch.Draw(internalTexture, destinationRectangle, Rectangle, Color.White);
        }
        #endregion

        #region To string
        /// <summary>
        /// To string.
        /// </summary>
        public override string ToString()
        {
            return "Texture(filename=" + filename +
                ", width=" + width +
                ", height=" + height +
                ", xnaTexture=" + (internalTexture != null ? "valid" : "null") + ")";
        }
        #endregion
    }
}
